<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>基于Object.defineProperty实现双向数据绑定</title>
</head>
<body>
	
	<h1 id="h1"></h1>
	<input type="text" id="inp" onkeyup="inputChange(event)">
  <button onclick="btnAdd()">加</button>
	
	
	<script>
		//数据源
		let vm = {
			value:0
		}
		
		//然后定义一个Dep，用于存储watcher
		let Dep = function(){
			this.list = [];
			this.add = function(watcher){
				this.list.push(watcher)
			},
			this.notify = function(newVal){
				this.list.forEach(function(fn){
					fn(newVal);
				})
			}
		}
		
		//模拟Compile出来的watchers，该demo涉及到两个地方的重新render，一个是title，另一个是输入框。所以写两个watcher，然后存入Dep
		// 模拟compile,通过对Html的解析生成一系列订阅者（watcher）
		function renderInput(newValue) {
			let el = document.getElementById('inp');
			if (el) {
				el.value = newValue
			}
		}

		function renderTitle(newValue) {
			let el = document.getElementById('h1');
			if (el) {
				el.innerHTML = newValue
			}
		}

		//将解析出来的watcher存入Dep中待用
		let dep = new Dep();
		dep.add(renderInput);
		dep.add(renderTitle)
		
		//使用 Object.defineProperty 定义一个Observer
		function observer(vm, key, value){
			Object.defineProperty(vm, key, {
				enumerable: true,
        configurable: true,
				get: function(){
					console.log('Get');
					return value;
				},
				set: function(newVal){
					if(value !== newVal){
						value = newVal;
						console.log('Update');
						
						//将变动通知给相关的订阅者
						dep.notify(newVal);
					}
				}
			})
		}
		
		
		//页面引用的方法
		function inputChange(ev) {
			let value = Number.parseInt(ev.target.value);
			vm.value = (Number.isNaN(value)) ? 0 : value;
		}
		function btnAdd() {
			vm.value = vm.value + 1;
		}
		
		
		
		//数据初始化方法
		function initMVVM(vm) {
			Object.keys(vm).forEach(function (key) {
				observer(vm, key, vm[key])
			})
		}

		//初始化数据源
		initMVVM(vm)

		//初始化页面，将数据源渲染到UI
		dep.notify(vm.value);
		
	</script>
</body>
</html>